package com.nnnu.orange.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nnnu.orange.constant.MessageConstants;
import com.nnnu.orange.constant.StatusConstants;
import com.nnnu.orange.exception.ObjectException;
import com.nnnu.orange.exception.TimeException;
import com.nnnu.orange.mapper.BooksMapper;
import com.nnnu.orange.mapper.BorrowingsMapper;
import com.nnnu.orange.mapper.UsersMapper;
import com.nnnu.orange.pojo.dto.ReservationDto;
import com.nnnu.orange.pojo.dto.ReservationsDto;
import com.nnnu.orange.pojo.dto.ReservationsQueryDto;
import com.nnnu.orange.pojo.dto.ReservationsUpdateDto;
import com.nnnu.orange.pojo.entity.Books;
import com.nnnu.orange.pojo.entity.Borrowings;
import com.nnnu.orange.pojo.entity.Reservations;
import com.nnnu.orange.mapper.ReservationsMapper;
import com.nnnu.orange.pojo.entity.Users;
import com.nnnu.orange.pojo.vo.ReservationsQueryVO;
import com.nnnu.orange.pojo.vo.ReservationsVO;
import com.nnnu.orange.result.PageResult;
import com.nnnu.orange.service.IReservationsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nnnu.orange.utils.ThreadLocalUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * <p>
 * 记录用户预约图书的信息 服务实现类
 * </p>
 *
 * @author zk
 * @since 2024-11-07
 */
@Service
@RequiredArgsConstructor
public class ReservationsServiceImpl extends ServiceImpl<ReservationsMapper, Reservations> implements IReservationsService {

    private final BooksMapper booksMapper;

    private final BorrowingsMapper borrowingsMapper;
    private final UsersMapper usersMapper;

    /**
     * 分页查询图书预约记录
     *
     * @param queryDto dto
     * @return 分页数据
     */
    @Override
    public PageResult<ReservationsQueryVO> list(@NotNull ReservationsQueryDto queryDto) {
        LocalDate startTime = queryDto.getStartTime();
        LocalDate endTime = queryDto.getEndTime();

        if (startTime != null && endTime != null && endTime.isBefore(startTime)) {
            throw new TimeException(MessageConstants.BEFORE);
        }

        LocalDateTime start = startTime != null ? startTime.atStartOfDay() : null;
        LocalDateTime end = endTime != null ? endTime.atTime(LocalTime.MAX) : null;


        Page<ReservationsQueryVO> reservationsPage = baseMapper.selectReservationsPage(new Page<>(queryDto.getPageNum(), queryDto.getPageSize()), queryDto, start, end);
        return new PageResult<>(reservationsPage.getTotal(), reservationsPage.getRecords());
    }

    /**
     * 修改预约记录
     *
     * @param reservationsDto dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateReservation(@NotNull ReservationsDto reservationsDto) {

        Reservations existingReservation = baseMapper.selectById(reservationsDto.getReservationId());
        if (existingReservation == null) {
            throw new ObjectException(MessageConstants.NULL); // 预约记录不存在
        }

        LocalDateTime pickUpDate = reservationsDto.getPickUpDate();

        if (pickUpDate != null) {

            LocalDateTime now = LocalDateTime.now();
            LocalDateTime todayAt7PM = LocalDateTime.of(LocalDate.now(), LocalTime.of(19, 0));

            if (now.isAfter(todayAt7PM)) {
                if (pickUpDate.isBefore(todayAt7PM)) {

                    throw new IllegalArgumentException("预约时间已经超过今天的19:00，请选择未来日期");
                }
            }

        }


        // 获取新图书 ID 和旧图书 ID
        Integer newBookId = reservationsDto.getBookId();

        if (newBookId != null) {
            Integer oldBookId = existingReservation.getBookId();
            if (!newBookId.equals(oldBookId)) {

                Books newBook = booksMapper.selectById(newBookId);
                if (newBook == null) {
                    throw new ObjectException("所选图书不存在");
                }
                if (newBook.getAvailableCopies() <= 0) {
                    throw new ObjectException("所选图书库存不足，无法修改预约");
                }


                newBook.setAvailableCopies(newBook.getAvailableCopies() - 1);
                int newBookUpdateCount = booksMapper.updateById(newBook);
                if (newBookUpdateCount < 1) {
                    throw new ObjectException("更新新图书库存失败，请重试");
                }


                Books oldBook = booksMapper.selectById(oldBookId);
                if (oldBook != null) {
                    oldBook.setAvailableCopies(oldBook.getAvailableCopies() + 1);
                    int oldBookUpdateCount = booksMapper.updateById(oldBook);
                    if (oldBookUpdateCount < 1) {
                        throw new ObjectException("恢复旧图书库存失败，请重试");
                    }
                }
            }

        }

        // 更新预约记录
        Reservations updatedReservation = BeanUtil.copyProperties(reservationsDto, Reservations.class).setUpdatedAt(LocalDateTime.now()).setStatus(StatusConstants.ZERO);
        int reservationUpdateCount = baseMapper.updateById(updatedReservation);
        if (reservationUpdateCount < 1) {
            throw new ObjectException("更新预约记录失败，请重试");
        }
    }

    /**
     * 查询预约详细信息
     *
     * @param id 预约id
     * @return 预约实体
     */
    @Override
    public ReservationsVO findById(Integer id) {
        return baseMapper.selectByReservationId(id);
    }


    /**
     * 用户预约图书
     *
     * @param dto dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reserveBook(@NotNull ReservationDto dto) {
        Long userId = ThreadLocalUtil.getId();
        LocalDate pickupDate = dto.getPickupDate();
        Integer bookId = dto.getBookId();
        Users user = usersMapper.selectById(userId);
        if (ObjectUtil.isNull(user)) {
            throw new ObjectException(MessageConstants.USER_NOT_FOUND);
        }
        Books book = booksMapper.selectById(bookId);
        if (book == null || book.getAvailableCopies() <= 0) {
            throw new ObjectException(MessageConstants.BOOK_RESERVATION_FAILED);
        }
        LocalDate today = LocalDate.now();
        LocalTime now = LocalTime.now();
        if (now.isAfter(LocalTime.of(19, 0)) && pickupDate.isEqual(today)) {
            throw new ObjectException("当前时间已超过19:00，无法选择当天取书");
        }
        if (pickupDate.isBefore(today) || pickupDate.isAfter(today.plusDays(3))) {
            throw new ObjectException("取书时间只能选择当天到后三天内");
        }
        List<Reservations> userReservations = baseMapper.selectList(new LambdaQueryWrapper<Reservations>().eq(Reservations::getUserId, userId)
                .eq(Reservations::getStatus, StatusConstants.ONE));
        int maxReservationLimit = Objects.equals(user.getMembershipType(), StatusConstants.ONE) ? 5 : StatusConstants.THREE; // 1 为会员
        if (userReservations.size() >= maxReservationLimit) {
            throw new ObjectException("预约数量已达上限，无法预约");
        }
        List<Borrowings> userBorrowings = borrowingsMapper.selectList(
                new LambdaQueryWrapper<Borrowings>().eq(Borrowings::getUserId, userId).eq(Borrowings::getStatus, StatusConstants.ONE));
        int maxBorrowingLimit = Objects.equals(user.getMembershipType(), StatusConstants.ONE) ? 5 : StatusConstants.THREE; // 1 为会员
        if (userBorrowings.size() >= maxBorrowingLimit) {
            throw new ObjectException("借阅数量已达上限，无法预约");
        }
        if (userReservations.stream().anyMatch(r -> r.getBookId().equals(bookId))) {
            throw new ObjectException("您已预约过此图书，不能重复预约");
        }
        Reservations reservation = new Reservations();
        reservation.setUserId(Math.toIntExact(userId)).setBookId(bookId).setReservationDate(LocalDateTime.now())
                .setStatusReason(MessageConstants.RESERVATION_SUCCESS).setPickUpDate(pickupDate.atTime(19, 0, 0))
                .setStatus(Reservations.STATUS_CONFIRMED); // 1 表示预约中
        int insertResult = baseMapper.insert(reservation);
        if (insertResult < 1) {
            throw new ObjectException("预约操作失败，请重试");
        }
        book.setAvailableCopies(book.getAvailableCopies() - 1);
        int updateResult = booksMapper.updateById(book);
        if (updateResult < 1) {
            throw new ObjectException("更新图书库存失败，请重试");
        }
    }

    /**
     * 用戶取消預約
     *
     * @param reservationId 預約ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelReservation(Integer reservationId) {

        Reservations reservations = baseMapper.selectById(reservationId);
        if (reservations == null) {
            throw new ObjectException(MessageConstants.RESERVATION_NULL); // 预约记录不存在
        }


        Integer status = reservations.getStatus();
        if (!Set.of(Reservations.STATUS_PENDING, Reservations.STATUS_CONFIRMED).contains(status)) {
            throw new ObjectException(MessageConstants.RESERVATION_STATUS_ERR); // 状态错误
        }


        int rows = baseMapper.update(
                null,
                new LambdaUpdateWrapper<Reservations>()
                        .eq(Reservations::getReservationId, reservationId)
                        .set(Reservations::getStatus, Reservations.STATUS_CANCELLED)
                        .set(Reservations::getStatusReason, MessageConstants.RESERVATION_USER_CANCEL)
        );
        if (rows < 1) {
            throw new ObjectException(MessageConstants.RESERVATION_STATUS_ERR); // 更新失败
        }
    }

}
